#include "U2Math.h"

#include "asm_math.h"



U2EG_NAMESPACE_USING



const u2real U2Math::POS_INFINITY   = std::numeric_limits<u2real>::infinity();
const u2real U2Math::NEG_INFINITY   = -std::numeric_limits<u2real>::infinity();
const u2real U2Math::PI             = u2real( 4.0 * atan( 1.0 ) );
const u2real U2Math::TWO_PI         = u2real( 2.0 * PI );
const u2real U2Math::HALF_PI        = u2real( 0.5 * PI );
const u2real U2Math::fDeg2Rad       = PI / u2real(180.0);
const u2real U2Math::fRad2Deg       = u2real(180.0) / PI;
const u2real U2Math::LOG2           = log(u2real(2.0));


U2Math::AngleUnit   U2Math::msAngleUnit         = AU_DEGREE;
int                 U2Math::mTrigTableSize;
u2real              U2Math::mTrigTableFactor;
u2real*             U2Math::mSinTable           = NULL;
u2real*             U2Math::mTanTable           = NULL;




//-----------------------------------------------------------------------
U2Math::U2Math( unsigned int trigTableSize )
{
    msAngleUnit = AU_DEGREE;
    mTrigTableSize = trigTableSize;
    mTrigTableFactor = mTrigTableSize / U2Math::TWO_PI;

    mSinTable = U2_ALLOC_T(u2real, mTrigTableSize, MEMCATEGORY_GENERAL);
    mTanTable = U2_ALLOC_T(u2real, mTrigTableSize, MEMCATEGORY_GENERAL);

    buildTrigTables();
}

//-----------------------------------------------------------------------
U2Math::~U2Math()
{
    U2_FREE(mSinTable, MEMCATEGORY_GENERAL);
    U2_FREE(mTanTable, MEMCATEGORY_GENERAL);
}

//-----------------------------------------------------------------------
void U2Math::buildTrigTables(void)
{
    // Build trig lookup tables
    // Could get away with building only PI sized Sin table but simpler this 
    // way. Who cares, it'll ony use an extra 8k of memory anyway and I like 
    // simplicity.
    u2real angle;
    for (int i = 0; i < mTrigTableSize; ++i)
    {
        angle = U2Math::TWO_PI * i / mTrigTableSize;
        mSinTable[i] = sin(angle);
        mTanTable[i] = tan(angle);
    }
}
//-----------------------------------------------------------------------	
u2real U2Math::SinTable (u2real fValue)
{
    // Convert range to index values, wrap if required
    int idx;
    if (fValue >= 0)
    {
        idx = int(fValue * mTrigTableFactor) % mTrigTableSize;
    }
    else
    {
        idx = mTrigTableSize - (int(-fValue * mTrigTableFactor) % mTrigTableSize) - 1;
    }

    return mSinTable[idx];
}
//-----------------------------------------------------------------------
u2real U2Math::TanTable (u2real fValue)
{
    // Convert range to index values, wrap if required
    int idx = int(fValue *= mTrigTableFactor) % mTrigTableSize;
    return mTanTable[idx];
}


bool U2Math::RealEqual( u2real a, u2real b, u2real tolerance )
{
    if (fabs(b-a) <= tolerance)
        return true;
    else
        return false;
}

//-----------------------------------------------------------------------
void U2Math::setAngleUnit(U2Math::AngleUnit unit)
{
    msAngleUnit = unit;
}

//-----------------------------------------------------------------------
U2Math::AngleUnit U2Math::getAngleUnit(void)
{
    return msAngleUnit;
}

//-----------------------------------------------------------------------
u2real U2Math::AngleUnitsToRadians(u2real angleunits)
{
    if (msAngleUnit == AU_DEGREE)
        return angleunits * fDeg2Rad;
    else
        return angleunits;
}

//-----------------------------------------------------------------------
u2real U2Math::RadiansToAngleUnits(u2real radians)
{
    if (msAngleUnit == AU_DEGREE)
        return radians * fRad2Deg;
    else
        return radians;
}

//-----------------------------------------------------------------------
u2real U2Math::AngleUnitsToDegrees(u2real angleunits)
{
    if (msAngleUnit == AU_RADIAN)
        return angleunits * fRad2Deg;
    else
        return angleunits;
}

//-----------------------------------------------------------------------
u2real U2Math::DegreesToAngleUnits(u2real degrees)
{
    if (msAngleUnit == AU_RADIAN)
        return degrees * fDeg2Rad;
    else
        return degrees;
}

//-----------------------------------------------------------------------
int U2Math::ISign (int iValue)
{
    return ( iValue > 0 ? +1 : ( iValue < 0 ? -1 : 0 ) );
}
//-----------------------------------------------------------------------
Radian U2Math::ACos (u2real fValue)
{
    if ( -1.0 < fValue )
    {
        if ( fValue < 1.0 )
            return Radian(acos(fValue));
        else
            return Radian(0.0);
    }
    else
    {
        return Radian(PI);
    }
}
//-----------------------------------------------------------------------
Radian U2Math::ASin (u2real fValue)
{
    if ( -1.0 < fValue )
    {
        if ( fValue < 1.0 )
            return Radian(asin(fValue));
        else
            return Radian(HALF_PI);
    }
    else
    {
        return Radian(-HALF_PI);
    }
}

//-----------------------------------------------------------------------
u2real U2Math::Sign (u2real fValue)
{
    if ( fValue > 0.0 )
        return 1.0;

    if ( fValue < 0.0 )
        return -1.0;

    return 0.0;
}

//-----------------------------------------------------------------------
u2real U2Math::InvSqrt(u2real fValue)
{
    return u2real(asm_rsq(fValue));
}
//-----------------------------------------------------------------------
u2real U2Math::UnitRandom ()
{
    return asm_rand() / asm_rand_max();
}

//-----------------------------------------------------------------------
u2real U2Math::RangeRandom (u2real fLow, u2real fHigh)
{
    return (fHigh-fLow)*UnitRandom() + fLow;
}

//-----------------------------------------------------------------------
u2real U2Math::SymmetricRandom ()
{
    return 2.0f * UnitRandom() - 1.0f;
}

/**********************@@@@@@@@@@@@@@@@@@@@@
//---------------------------------------------------------------------
U2Matrix4 U2Math::makeViewMatrix(const U2Vector3& position, const U2Quaternion& orientation, 
                             const U2Matrix4* reflectMatrix)
{
    U2Matrix4 viewMatrix;

    // View matrix is:
    //
    //  [ Lx  Uy  Dz  Tx  ]
    //  [ Lx  Uy  Dz  Ty  ]
    //  [ Lx  Uy  Dz  Tz  ]
    //  [ 0   0   0   1   ]
    //
    // Where T = -(Transposed(Rot) * Pos)

    // This is most efficiently done using 3x3 Matrices
    U2Matrix3 rot;
    orientation.ToRotationMatrix(rot);

    // Make the translation relative to new axes
    U2Matrix3 rotT = rot.Transpose();
    Vector3 trans = -rotT * position;

    // Make final matrix
    viewMatrix = U2Matrix4::IDENTITY;
    viewMatrix = rotT; // fills upper 3x3
    viewMatrix[0][3] = trans.x;
    viewMatrix[1][3] = trans.y;
    viewMatrix[2][3] = trans.z;

    // Deal with reflections
    if (reflectMatrix)
    {
        viewMatrix = viewMatrix * (*reflectMatrix);
    }

    return viewMatrix;

}
*/